<?php

/***  DOCUMENTATION LAYER

Klenwell CEO Form Class

Name: CeoForm
Last Update: Sep 2007
Author: Tom at klenwell@gmail.com

DESCRIPTION
  Briefly describe class here

METHODS
  MAGIC
  CeoForm($debug=0, $oid=null)    *php 4 constructor*
  __construct($debug, $oid)       *php 5 constructor*
  __destruct()  
  
  PUBLIC
  is_submitted()
  is_valid()
  add_element($name, $type, $label, $is_required=0, $arg5=null, $arg6=null, $arg7=null, $arg8=null)
  add_button($type, $label='')
  sanitize_input($text, $TAG=array()
  reset_form()
  print_d($message, $color='#c33')
  print_r($Mixed)
  dump()
  
  PRIVATE
  _is_reset_request()
  _set_session_data()
  _get_session_data()
  _has_session_data()
  _set_filename()
  
  
USAGE
  $CeoForm = new CeoForm();
  $CeoForm->print_r('hello world']);
  
  * To sanitize input:
  $clean_input = CeoForm::sanitize_input($input, $TAG=array(), $ATTR=array());

NOTES
  

______________________________________________________________________________*/

// Load File of Base Class
// n/a


// CeoForm
/*____________________________________________________________________________*/
class CeoForm
{
/* PUBLIC PROPERTIES */
var $debug = 0;
var $class_name = __CLASS__;
var $oid = '';
var $DS = DIRECTORY_SEPARATOR;

// handler arrays
var $EL = array();      // element objects
var $VALID = array();   // validation arrays
var $WHITE = array();   // input filter whitelists

// objects
var $InputFilter = 0;    // InputFilter Object

// dependent file name
var $element_factory_fname = 'element.factory.php';
var $InputFilter_file = 'class.inputfilter.php';

// properties
var $is_submitted = 0;
var $is_reset_request = 0;

// form trigger cues 
// (these should be unique enough that they aren't accidentally chosen for element names by developer)
var $form_submit_trigger = 'xCeoFormTriggerSubmitx';
var $form_reset_trigger = 'xCeoFormTriggerResetx';


/* PRIVATE PROPERTIES */
var $_is_valid = 0;

var $_filename = '';
var $_dirpath = '';



/* ** MAGIC METHODS ** */
// php4 constructor
function CeoForm($debug=0, $oid=null)
{
  $this->__construct($debug, $oid);
  register_shutdown_function( array($this, '__destruct') );
}
// END constructor

// php5 constructor
function __construct($debug=0, $oid=null)
{
  // defaults
  $this->debug = $debug;
  $this->oid = ( empty($oid) ) ? $this->class_name : $oid;
  $this->_set_filename();
  $this->_set_dirpath();
  
  // validation arrays
  $this->VALID['BX'] = array( 'submit', 'reset' );
  
  // whitelists
  $this->WHITE['TAG'] = array(
    'div', 'p', 'blockquote', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
    'span', 'a', 'b', 'i', 'em', 'strong', 'code', 'sub', 'sup' );
  $this->WHITE['ATT'] = array('href', 'title', 'cite');
  
  // path values
  $this->_ceo_root = dirname(dirname($this->_dirpath)) . $this->DS;
  $this->_ifo_dir = $this->_ceo_root . 'ext_oss' . $this->DS . 'input_filter' . $this->DS;
  $this->element_factory_path = $this->element_factory_fname;
  $this->InputFilter_path = $this->_ifo_dir . $this->InputFilter_file;
  #if ( PHP_VERSION >= 5 ) $this->InputFilter_path = $this->_ifo_dir . 'class.inputfilter.php5';  # php5 version restricts access to some properties we need
  
  // load dependencies
  require_once($this->element_factory_path);
  
  // load filter object
  $this->_load_input_filter();
  
  // has form been submitted or reset?
  if ( $this->_is_reset_request() ) $this->reset_form();
  
  // debug
  if ( $this->debug ) $this->print_d('debugging is active for oid ' . $this->oid);
  if ( $this->debug ) $this->print_d('constructor complete for class ' . __CLASS__);
}
// END constructor

// destructor
function __destruct()
{
  if ( $this->debug ) $this->print_d("destroying class {$this->class_name} (oid: {$this->oid})");
}
// END destructor



/* ** PUBLIC METHODS ** */
// method: is form submitted
function is_submitted()
{
  $is_submitted = 0;
  if ( !empty($_POST[$this->form_submit_trigger]) ) $is_submitted = 1;
  return $is_submitted;
}

// method: is valid
function is_valid()
{
  $is_valid = 1;  // return value
  $debug_str = '';
  
  foreach ( $this->EL as $name => $_Object )
  {
    if ( $this->debug )  $debug_str .= " {$name}={$_Object->is_valid} ";
    if ( !$_Object->is_valid ) { $is_valid = 0; break; }
  }
  
  if ( $this->debug )  $this->print_d("form is valid: $is_valid [$debug_str]", 'blue');
  return $is_valid;
}
// END method


// method: get data
function get_data()
{
  $DATA = array();    // return
  
  foreach ( $this->EL as $name => $_Object )
  {
    $DATA[$name] = ( isset($_Object->valid_input) ) ? $_Object->valid_input : null;
  }
  
  return $DATA;
}
// END method


// method: get data
function get_valid_data()
{
  return $this->get_data();
}
// END method


// method: add element
function add_element($name, $type, $label, $is_required=0, $arg5=null, $arg6=null, $arg7=null, $arg8=null)
{
  // register element
  if ( isset($this->EL[$name]) )
  {
    trigger_error("an element with name [$name] has already been added to this form -- each element must have a unique name", E_USER_WARNING);
    return 0; 
  }
  else
  {
    $this->EL[$name] = 0;
  }
  
  // create object
  $ElementFactory = new CeoFormElementFactory($name, $type, $label, $is_required, $arg5, $arg6, $arg7, $arg8, $this->debug);
  
  // this will return the element-type object to the array handler
  /* note: processing done via the element constructor (invoked here), so we
    cannot make pre-processing adjustments here (as I wished to do) unless I
    break the process step out of the cx and add it as an explicit call here.
    Perhaps, in the future, but for now, I'm going to try to avoid it. */
  $this->EL[$name] = $ElementFactory->make();

  return 1;
}
// END method

// method: add_button
function add_button($type, $label='')
{
  $html = '';

  // check type
  if ( !in_array($type, $this->VALID['BX']) )
  {
    trigger_error("invalid button type: [$type]", E_USER_WARNING);
    return 0;
  }
  
  // set input names (triggers)
  if ( $type == 'submit' ) $name = $this->form_submit_trigger;
  elseif ( $type == 'reset' ) $name = $this->form_reset_trigger;
  
  // default label
  if ( empty($label) ) $label = $type;
  
  // build button
  $html = '<input id="' .$name. '" type="' .$type. '" name="' .$name. '" value="' .$label. '" />';
  
  // trigger  
    
  return $html;
}
// END method


// method: sanitize input
// taglist/attrlists are white by default (only specified values allows, all else excluded)
function filter_input($text, $TAG=array(), $ATTR=array())
{  
  $sanitized_text = '';    // return
  
  if ( !$this->InputFilter ) $this->_load_input_filter();
  
    // backup default TAG / ATTR properties
  $TAG_BACK = $this->InputFilter->tagsArray;
  $ATTR_BACK = $this->InputFilter->attrArray;
  
  // update TAG / ATTR properties
  if ( !empty($TAG) ) $this->InputFilter->tagsArray = $TAG;
  if ( !empty($ATTR) ) $this->InputFilter->attrArray = $ATTR;
  
  // sanitize  
  $sanitized_text = $this->InputFilter->process($text);
  
  // return whitelists to defaults
  $this->InputFilter->tagsArray = $TAG_BACK;
  $this->InputFilter->attrArray = $ATTR_BACK;
  
  // return  
  return $sanitized_text;
}
// END method


// method: static input filter for external (viz. subclass) use (by whitelist)
function staticInputFilter($text, $TAG=array(), $ATT=array(), $debug=0)
{  
  $sanitized_text = '';    // return
  
  global $CEO;            // framework global
  static $_InputFilter;   // static singleton class variable
  
  // classpath
  $_s = DIRECTORY_SEPARATOR;
  $_dir = dirname(dirname(dirname(__FILE__))) . $_s . "ext_oss{$_s}input_filter" . $_s;
  $_fname = 'class.inputfilter.php';
  
  // get instance of class
  if ( !is_object($_InputFilter) )
  {
    require_once($_dir . $_fname);
    $_InputFilter = new InputFilter();
    if ( $debug ) CeoForm::print_d('InputFilter created');
  }
  else
  {
    if ( $debug ) CeoForm::print_d('InputFilter already exists', '#99f');
  }
  
  // update TAG / ATTR properties
  #trigger_error('trace', E_USER_WARNING);  // debug
  $_InputFilter->tagsArray = ( !empty($TAG) ) ? $TAG : array();
  $_InputFilter->attrArray = ( !empty($ATT) ) ? $ATT : array();
  
  // sanitize  
  $sanitized_text = $_InputFilter->process($text);

  // return  
  return $sanitized_text;
}
// END method


// method: reset form
function reset()
{
  trigger_error(__FUNCTION__ . ' in dev');
  if ( $this->debug ) $this->print_d('resetting form');
  
  // cycle through all form object
  foreach ( $this->EL as $name => $_Object ) $_Object->reset();
  
  return;
}
// END method

// method: print_d
function print_d($message, $color='#c33')
{
  $out = "<div style='line-height:1.5em; font-family:monospace; color:$color;'>$message</div>";
  echo $out;
  return;
}
// END method

// method: print_r
function print_r($Mixed)
{
  $return = htmlspecialchars(print_r($Mixed, 1));
  $return = "<pre>$return</pre>";
  return $return;
}
// END method

// method: dump
function dump()
{
  echo $this->print_r($this);
  return;
}
// END method



/* ** PRIVATE METHODS ** */
// method: load input filter
function _load_input_filter()
{
  if ( $this->debug ) $this->print_d('loading InputFilter');
  
  // get instance of class
  if ( !is_object($this->InputFilter) )
  {
    require_once($this->InputFilter_path);
    $this->InputFilter = new InputFilter();
    if ( $this->debug ) $this->print_d('InputFilter loaded');
  }
  else
  {
    if ( $this->debug ) $this->print_d('InputFilter already loaded', '#99f');
  }
  
  $this->InputFilter->tagsArray = $this->WHITE['TAG'];
  $this->InputFilter->attrArray = $this->WHITE['ATT'];
}


// method: is form reset
function _is_reset_request()
{
  $is_request = 0;
  if ( !empty($_POST[$this->form_reset_trigger]) ) $is_request = 1;
  return $is_request;
}

// method: _set_session_data
function _set_session_data()
{
  // initialize session
  if ( !session_id() ) session_start(); 
  $_SESSION[$this->oid] = array();
  
  return;
}
// END method

// method: get session data
function _get_session_data()
{
  // initialize session
  if ( !$this->_has_session_data() ) return; 
    
  // retrieve session variables
  // $this->var = $_SESSION[$this->oid]['var'];
  
  return;
}
// END method

// method: has session data
function _has_session_data()
{
  // initialize session
  if ( !session_id() ) session_start(); 
    
  // retrieve session variables
  if ( empty($_SESSION[$this->oid]) ) return 0;
  else return 1;
}
// END method

function _set_filename() { $this->_filename = basename(__FILE__); }
function _set_dirpath() { $this->_dirpath = dirname(__FILE__) . $this->DS; }

} // end class
/*____________________________________________________________________________*/

?>
